package ie.ucd.ABLE.MachineLearning;

import ie.ucd.ABLE.MachineLearning.MySQLConnect;
import ie.ucd.ABLE.MachineLearning.EpiSensorData;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.FileReader;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Random;
import java.util.TimeZone;

import weka.classifiers.Evaluation;
import weka.classifiers.meta.FilteredClassifier;
import weka.classifiers.trees.*;
import weka.classifiers.bayes.*;
import weka.classifiers.meta.*;
import weka.classifiers.misc.VFI;
import weka.core.Instances;
import weka.classifiers.lazy.*;
import weka.filters.unsupervised.attribute.Remove;


public class MachineLearningABLE {

	private ArrayList<EpiSensorData> epiSensorData;
	private ArrayList<EpiSensorData> epiSensorTrainingData;
	private ArrayList<HeterogeneousSensorData> heterogeneousSensorData;
	private ArrayList<HeterogeneousSensorData> heterogeneousTrainingSetData;
	private String lastRecordReadDate;
	private MySQLConnect mySQLDB;
	private java.util.Calendar cal;
	private int DATA_COUNTER;
	private int LIMIT_DATA_LOADING;
	private int base_power;

	// Step change variables...
	private static final int SHOWER_MAXIMUM = 8700;
	private static final int SHOWER_MINIMUM = 8200;

	private static final int WATERHEATER_MAXIMUM = 2800;
	private static final int WATERHEATER_MINIMUM = 2600;

	//	private static final int DRYER_MAXIMUM = 2750;
	//	private static final int DRYER_MINIMUM = 2650;	

	private static final int OVEN_MAXIMUM = 2390;
	private static final int OVEN_MINIMUM = 2250;

	private static final int KETTLE_MAXIMUM = 2000;
	private static final int KETTLE_MINIMUM = 1800;

	private static final int STOVE6_MAXIMUM = 1610;
	private static final int STOVE6_MINIMUM = 1510;

	private static final int MICROWAVE_MAXIMUM = 1500;
	private static final int MICROWAVE_MINIMUM = 1250;

	private static final int TOASTER_MAXIMUM = 790;
	private static final int TOASTER_MINIMUM = 730;

	private static final int FRIDGE_MAXIMUM = 160;
	private static final int FRIDGE_MINIMUM = 90;

	private static final int BATHROOMLIGHT_MAXIMUM = 89;
	private static final int BATHROOMLIGHT_MINIMUM = 75;

	private static final int MIX_MAXIMUM = 74;
	private static final int MIX_MINIMUM = 25;

	private static final int ROOMLIGHT_MAXIMUM = 25;
	private static final int ROOMLIGHT_MINIMUM = 12;

	//	private static final int STOVE5_MAXIMUM = 1490;
	//	private static final int STOVE5_MINIMUM = 1400;
	//
	//	private static final int STOVE4_MAXIMUM = 1200;
	//	private static final int STOVE4_MINIMUM = 1100;
	//
	//	private static final int BASE_MAXIMUM = 50;
	//	private static final int BASE_MINIMUM = 40;

	public MachineLearningABLE(){
		base_power=44;
		DATA_COUNTER = 0;
		LIMIT_DATA_LOADING = 1000000;
		//set Timezone to GMT
		cal = Calendar.getInstance();
		cal.setTimeZone(TimeZone.getTimeZone("GMT"));

		epiSensorData = new ArrayList<EpiSensorData>();
		epiSensorTrainingData = new ArrayList<EpiSensorData>();		
		heterogeneousSensorData = new ArrayList<HeterogeneousSensorData>();
		heterogeneousTrainingSetData = new ArrayList<HeterogeneousSensorData>();
		lastRecordReadDate = null;		
	}

	public void connectWithMysqlServer(){
		//Connect to My SQL Server...
		mySQLDB = new MySQLConnect("able1.ucd.ie", "able_db", "ableDbP@ssword", "able");

		if (mySQLDB.getDbConnection()==null){
			mySQLDB = new MySQLConnect("able1.ucd.ie", "able_db", "ableDbP@ssword", "able");
		}else{
			System.out.println("Connected to MySQL Server...");
		}		
	}

	public void retreiveRecordsAndFillArrayList(String query, String sensorType){
		//to check memory usage of java...
		MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
		int MEGABYTE = (1024*1024);
		//variable declaration to be used while retreiving mysql data...
		ResultSet resultS;
		double SensedValue; 
		String SensorType;
		int NodeType;
		int ModalityType;
		String ApplianceInfo;
		Timestamp SenseDate = null; 
		try {		
			resultS = mySQLDB.queryTable(query);
			if(sensorType.equalsIgnoreCase("episensor")){	//if its epiSensor do this
				DATA_COUNTER = 0;
				if(resultS.next()){
					do {
						SensedValue = resultS.getInt("SenseValue");
						SensorType = resultS.getString("Name");
						SenseDate = resultS.getTimestamp("SenseDate", cal);
						epiSensorData.add(new EpiSensorData(SenseDate, SensorType, (int)SensedValue, null));
						DATA_COUNTER++;
					}while (resultS.next() && DATA_COUNTER < LIMIT_DATA_LOADING); //Retreive Not more than 10,00,000 records at a time...
					//close result set now...
					resultS.close();
					System.out.println(DATA_COUNTER+ " Episensor unlabeled data retrieved...");
				}else{
					System.out.println("No EpiSensor data to be retrieved...");
				}
			}else if(sensorType.equalsIgnoreCase("episensor_training")){	//if its episensor training data set do this
				DATA_COUNTER = 0;
				if(resultS.next()){
					do {
						SensedValue = resultS.getInt("SensorValue");
						SensorType = resultS.getString("SensorType");
						SenseDate = resultS.getTimestamp("DateTime", cal);
						ApplianceInfo = resultS.getString("ApplianceOn");
						epiSensorTrainingData.add(new EpiSensorData(SenseDate, SensorType, (int)SensedValue, ApplianceInfo));
						DATA_COUNTER++;
					}while (resultS.next() && DATA_COUNTER < LIMIT_DATA_LOADING); //Retreive Not more than 10,00,000 records at a time...
					//close result set now...
					resultS.close();
					System.out.println(DATA_COUNTER+ " Episensor Training set records retrieved...");
				}else{
					System.out.println("No EpiSensor training data to be retrieved...");
				}
			}
			else if(sensorType.equalsIgnoreCase("heterogeneoussensor")){	//if its heterogeneousSensor do this
				DATA_COUNTER = 0;
				if(resultS.next()){
					do {
						SensedValue = resultS.getDouble("SensorValue");
						NodeType = resultS.getInt("NodeID");
						SenseDate = resultS.getTimestamp("SenseDateTime", cal);
						ModalityType = resultS.getInt("ModalityID");
						heterogeneousSensorData.add(new HeterogeneousSensorData(SenseDate, NodeType, ModalityType, SensedValue, null));
						DATA_COUNTER++;
					}while (resultS.next() && DATA_COUNTER < LIMIT_DATA_LOADING); //Retreive Not more than 10,00,000 records at a time...
					//close result set now...
					resultS.close();
					System.out.println(DATA_COUNTER+ " Records retrieved...");
				}else{
					System.out.println("No Heterogeneous data to be retrieved...");
				}
			}else if(sensorType.equalsIgnoreCase("heterogeneoussensor_training")){	//if its heterogeneousSensor do this
				DATA_COUNTER = 0;
				if(resultS.next()){
					do {
						SensedValue = resultS.getDouble("SensorValue");
						NodeType = resultS.getInt("NodeID");
						SenseDate = resultS.getTimestamp("DateTime", cal);
						ModalityType = resultS.getInt("ModalityID");
						ApplianceInfo = resultS.getString("ApplianceOn");
						heterogeneousTrainingSetData.add(new HeterogeneousSensorData(SenseDate, NodeType, ModalityType, SensedValue, ApplianceInfo));
						DATA_COUNTER++;
					}while (resultS.next() && DATA_COUNTER < LIMIT_DATA_LOADING); //Retreive Not more than 10,00,000 records at a time...
					//close result set now...
					resultS.close();
					System.out.println(DATA_COUNTER+ " Records retrieved...");
				}else{
					System.out.println("No Heterogeneous data to be retrieved...");
				}
			}else{
				System.out.println("Nothing retreived...");		//if its neither of above	
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}catch (OutOfMemoryError e){
			MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
			long maxMemory = heapUsage.getMax() / MEGABYTE;
			long usedMemory = heapUsage.getUsed() / MEGABYTE;
			System.out.println(" : Memory Used :" + usedMemory + "M/" + maxMemory + "M");
		}
	}

	public void saveDataAsARFF(){
		double testTotalWatts = 0;
		try{
			//FileWriter fstream = new FileWriter("episensor_data.csv");
			BufferedWriter epiSensorWriter = new BufferedWriter(new FileWriter("episensor_unlabeled_data.arff"));
			BufferedWriter epiSensorTrainingSetWriter = new BufferedWriter(new FileWriter("episensor_realpower_training_data.arff"));
			BufferedWriter heterogeneousSensorWriter = new BufferedWriter(new FileWriter("heterogeneous_sensor_unlabeled_data.arff"));
			BufferedWriter heterogeneousTrainingSetWriter = new BufferedWriter(new FileWriter("heterogeneous_sensor_training_data.arff"));

			if(epiSensorTrainingData.size()>0){
				//arff headers...
				epiSensorTrainingSetWriter.write("@relation appliance");
				epiSensorTrainingSetWriter.newLine();
				epiSensorTrainingSetWriter.newLine();
				epiSensorTrainingSetWriter.write("@attribute dateTime date 'yyyy-MM-dd HH:mm:ss.S'");
				epiSensorTrainingSetWriter.newLine();
				epiSensorTrainingSetWriter.write("@attribute sensorType {'Real Power'}");
				epiSensorTrainingSetWriter.newLine();
				epiSensorTrainingSetWriter.write("@attribute sensorValue numeric");
				epiSensorTrainingSetWriter.newLine();
				epiSensorTrainingSetWriter.write("@attribute applianceOn {base,fridge,shower,water_heater,oven,kettle,stove,microwave,toaster,washing_machine,bathroom_light,room_light,base_extras}");
				epiSensorTrainingSetWriter.newLine();
				epiSensorTrainingSetWriter.newLine();
				epiSensorTrainingSetWriter.write("@data");
				epiSensorTrainingSetWriter.newLine();

				for (int i = 0; i < epiSensorTrainingData.size(); i++) {
					EpiSensorData sensorData = (EpiSensorData) epiSensorTrainingData.get(i);
					epiSensorTrainingSetWriter.write("'"+sensorData.getSenseDate()+"','"+sensorData.getSensorType()+"',"+sensorData.getSenseValue()+","+sensorData.getApplianceInfo());
					epiSensorTrainingSetWriter.newLine();
				}
				System.out.println("EpiSensor training data set saved into arff file...");
				epiSensorTrainingSetWriter.close();
			} if(epiSensorData.size()>0){
				//arff headers...
				epiSensorWriter.write("@relation appliance");
				epiSensorWriter.newLine();
				epiSensorWriter.newLine();
				epiSensorWriter.write("@attribute dateTime date 'yyyy-MM-dd HH:mm:ss.S'");
				epiSensorWriter.newLine();
				epiSensorWriter.write("@attribute sensorType {'Real Power'}");
				epiSensorWriter.newLine();
				epiSensorWriter.write("@attribute sensorValue numeric");
				epiSensorWriter.newLine();
				epiSensorWriter.write("@attribute applianceOn {base,fridge,shower,water_heater,oven,kettle,stove,microwave,toaster,washing_machine,bathroom_light,room_light,base_extras}");
				epiSensorWriter.newLine();
				epiSensorWriter.newLine();
				epiSensorWriter.write("@data");
				epiSensorWriter.newLine();

				for (int i = 0; i < epiSensorData.size(); i++) {
					int previous = 0;
					if(i!=0){
						previous = i-1;
					}else{
						previous = i;
					}
					EpiSensorData pSensorData = (EpiSensorData) epiSensorData.get(previous);
					EpiSensorData sensorData = (EpiSensorData) epiSensorData.get(i);

					//call appliance check method to see if there's a step change in real power...
					List<Integer> stepChanges = (List<Integer>)checkAppliancesTurnedOn((int)pSensorData.getSenseValue(), (int)sensorData.getSenseValue());
					//					stepChanges = null;
					if(stepChanges!=null){
						for (int j = 0; j < stepChanges.size(); j++){
							System.out.println("Inside loop: "+sensorData.getSenseDate()+"  "+stepChanges.get(j));
							epiSensorWriter.write("'"+sensorData.getSenseDate()+"','"+sensorData.getSensorType()+"',"+stepChanges.get(j)+",?");
							epiSensorWriter.newLine();
							testTotalWatts = testTotalWatts+ stepChanges.get(j);
						}
					}else{
						epiSensorWriter.write("'"+sensorData.getSenseDate()+"','"+sensorData.getSensorType()+"',"+sensorData.getSenseValue()+",?");
						epiSensorWriter.newLine();
						testTotalWatts = testTotalWatts + sensorData.getSenseValue();
					}
				}
				System.out.println("EpiSensor data saved into arff file...");
				System.out.println("Total watts: "+testTotalWatts);
				//Close the file
				epiSensorWriter.close();
			} if(heterogeneousSensorData.size()>0){
				//arff headers...
				heterogeneousSensorWriter.write("@relation appliance");
				heterogeneousSensorWriter.newLine();
				heterogeneousSensorWriter.newLine();
				heterogeneousSensorWriter.write("@attribute dateTime date 'yyyy-MM-dd HH:mm:ss.S'");
				heterogeneousSensorWriter.newLine();
				heterogeneousSensorWriter.write("@attribute nodeType numeric");
				heterogeneousSensorWriter.newLine();
				heterogeneousSensorWriter.write("@attribute sensorType numeric");
				heterogeneousSensorWriter.newLine();
				heterogeneousSensorWriter.write("@attribute sensorValue real");
				heterogeneousSensorWriter.newLine();
				heterogeneousSensorWriter.write("@attribute applianceOn {light_on,light_off}");
				heterogeneousSensorWriter.newLine();
				heterogeneousSensorWriter.newLine();
				heterogeneousSensorWriter.write("@data");
				heterogeneousSensorWriter.newLine();

				for (int i = 0; i < heterogeneousSensorData.size(); i++) {
					//					System.out.println(heterogeneousSensorData.get(i).getSenseDateTime()+"    "+heterogeneousSensorData.get(i).getSenseValue());
					HeterogeneousSensorData sensorData = (HeterogeneousSensorData) heterogeneousSensorData.get(i);
					heterogeneousSensorWriter.write("'"+sensorData.getSenseDateTime()+"',"+sensorData.getNodeType()+","+sensorData.getModalityType()+","+sensorData.getSenseValue()+",?");
					heterogeneousSensorWriter.newLine();
				}
				System.out.println("Heterogeneous sensor data saved into arff file...");
				heterogeneousSensorWriter.close();
			} if(heterogeneousTrainingSetData.size()>0){

				//arff headers...
				heterogeneousTrainingSetWriter.write("@relation appliance");
				heterogeneousTrainingSetWriter.newLine();
				heterogeneousTrainingSetWriter.newLine();
				heterogeneousTrainingSetWriter.write("@attribute dateTime date 'yyyy-MM-dd HH:mm:ss.S'");
				heterogeneousTrainingSetWriter.newLine();
				heterogeneousTrainingSetWriter.write("@attribute nodeType numeric");
				heterogeneousTrainingSetWriter.newLine();
				heterogeneousTrainingSetWriter.write("@attribute sensorType numeric");
				heterogeneousTrainingSetWriter.newLine();
				heterogeneousTrainingSetWriter.write("@attribute sensorValue real");
				heterogeneousTrainingSetWriter.newLine();
				heterogeneousTrainingSetWriter.write("@attribute applianceOn {light_on,light_off}");
				heterogeneousTrainingSetWriter.newLine();
				heterogeneousTrainingSetWriter.newLine();
				heterogeneousTrainingSetWriter.write("@data");
				heterogeneousTrainingSetWriter.newLine();

				for (int i = 0; i < heterogeneousTrainingSetData.size(); i++) {
					//					System.out.println(heterogeneousSensorData.get(i).getSenseDateTime()+"    "+heterogeneousSensorData.get(i).getSenseValue());
					HeterogeneousSensorData sensorData = (HeterogeneousSensorData) heterogeneousTrainingSetData.get(i);
					heterogeneousTrainingSetWriter.write("'"+sensorData.getSenseDateTime()+"',"+sensorData.getNodeType()+","+sensorData.getModalityType()+","+sensorData.getSenseValue()+","+sensorData.getApplianceInfo());
					heterogeneousTrainingSetWriter.newLine();
				}
				System.out.println("Heterogeneous training data set saved into arff file...");
				heterogeneousTrainingSetWriter.close();
			}
			//			if(epiSensorData.size()==0 ){
			//				System.out.println("Nothing retreived so nothing to be saved in arff files...");	
			//			}
		} catch (IOException e) {
			System.out.println("Could not write new date into file...");
			e.printStackTrace();
		}
	}

	public void performCalculationsAndSave(){



	}

	public List<Integer> checkAppliancesTurnedOn(int previousRealPower, int currentRealPower){
		//perform step change stuff here and return watts per appliance in an array...
		List<Integer> stepChanges = new ArrayList<Integer>();

		if(currentRealPower > base_power+10){
			int sDifference = currentRealPower-base_power;
			//now check if appliance is turned on or off... should be greater than 0 if on and less than 0 if off
			if(sDifference>=0){
				//An appliance has been turned on... now check which one...
				//start checking from the largerst energy consumer device...
				//add base...
				stepChanges.add(base_power);
				//if this is shower
				if(sDifference >= SHOWER_MINIMUM){
					if(sDifference <= SHOWER_MAXIMUM){
						stepChanges.add(sDifference);
						sDifference = 0;
					}else{
						sDifference = sDifference-(SHOWER_MINIMUM+SHOWER_MAXIMUM)/2;
						stepChanges.add((SHOWER_MINIMUM+SHOWER_MAXIMUM)/2);
					}
				}
				//if this is water heater
				if(sDifference >= WATERHEATER_MINIMUM){
					if(sDifference <= WATERHEATER_MAXIMUM){
						stepChanges.add(sDifference);
						sDifference = 0;
					}else{
						sDifference = sDifference-(WATERHEATER_MINIMUM+WATERHEATER_MAXIMUM)/2;
						stepChanges.add((WATERHEATER_MINIMUM+WATERHEATER_MAXIMUM)/2);
					}
				}
				//				//if this is dryer
				//				if(sDifference >= DRYER_MINIMUM){
				//					if(sDifference <= DRYER_MAXIMUM){
				//						stepChanges.add(sDifference);
				//						sDifference = 0;
				//					}else{
				//						sDifference = sDifference-(DRYER_MINIMUM+DRYER_MAXIMUM)/2;
				//						stepChanges.add((DRYER_MINIMUM+DRYER_MAXIMUM)/2);
				//					}
				//				}
				//if this is oven
				if(sDifference >= OVEN_MINIMUM){
					if(sDifference <= OVEN_MAXIMUM){
						stepChanges.add(sDifference);
						sDifference = 0;
					}else{
						sDifference = sDifference-(OVEN_MINIMUM+OVEN_MAXIMUM)/2;
						stepChanges.add((OVEN_MINIMUM+OVEN_MAXIMUM)/2);
					}
				}
				//if this is kettle
				if(sDifference >= KETTLE_MINIMUM){
					if(sDifference <= KETTLE_MAXIMUM){
						stepChanges.add(sDifference);
						sDifference = 0;
					}else{
						sDifference = sDifference-(KETTLE_MINIMUM+KETTLE_MAXIMUM)/2;
						stepChanges.add((KETTLE_MINIMUM+KETTLE_MAXIMUM)/2);
					}
				}
				//if this is stove at 6
				if(sDifference >= STOVE6_MINIMUM){
					if(sDifference <= STOVE6_MAXIMUM){
						stepChanges.add(sDifference);
						sDifference = 0;
					}else{
						sDifference = sDifference-(STOVE6_MINIMUM+STOVE6_MAXIMUM)/2;
						stepChanges.add((STOVE6_MINIMUM+STOVE6_MAXIMUM)/2);
					}
				}				
				//if this is microwave
				if(sDifference >= MICROWAVE_MINIMUM){
					if(sDifference <= MICROWAVE_MAXIMUM){
						stepChanges.add(sDifference);
						sDifference = 0;
					}else{
						sDifference = sDifference-(MICROWAVE_MINIMUM+MICROWAVE_MAXIMUM)/2;
						stepChanges.add((MICROWAVE_MINIMUM+MICROWAVE_MAXIMUM)/2);
					}
				}
				//if this is toaster
				if(sDifference >= TOASTER_MINIMUM){
					if(sDifference <= TOASTER_MAXIMUM){
						stepChanges.add(sDifference);
						sDifference = 0;
					}else{
						sDifference = sDifference-(TOASTER_MINIMUM+TOASTER_MAXIMUM)/2;
						stepChanges.add((TOASTER_MINIMUM+TOASTER_MAXIMUM)/2);
					}
				}
				//if this is fridge
				if(sDifference >= FRIDGE_MINIMUM){
					if(sDifference <= FRIDGE_MAXIMUM){
						stepChanges.add(sDifference);
						sDifference = 0;
					}else{
						sDifference = sDifference-(FRIDGE_MINIMUM+FRIDGE_MAXIMUM)/2;
						stepChanges.add((FRIDGE_MINIMUM+FRIDGE_MAXIMUM)/2);
					}
				}
				//if this is bathroom light
				if(sDifference >= BATHROOMLIGHT_MINIMUM){
					if(sDifference <= BATHROOMLIGHT_MAXIMUM){
						stepChanges.add(sDifference);
						sDifference = 0;
					}else{
						sDifference = sDifference-(BATHROOMLIGHT_MINIMUM+BATHROOMLIGHT_MAXIMUM)/2;
						stepChanges.add((BATHROOMLIGHT_MINIMUM+BATHROOMLIGHT_MAXIMUM)/2);
					}
				}
				//if this is mix load i.e charger, lights, etc...
				if(sDifference >= MIX_MINIMUM){
					if(sDifference <= MIX_MAXIMUM){
						stepChanges.add(sDifference);
						sDifference = 0;
					}else{
						sDifference = sDifference-(MIX_MINIMUM+MIX_MAXIMUM)/2;
						stepChanges.add((MIX_MINIMUM+MIX_MAXIMUM)/2);
					}
				}
				//if this is room light
				if(sDifference >= ROOMLIGHT_MINIMUM){
					if(sDifference <= ROOMLIGHT_MAXIMUM){
						stepChanges.add(sDifference);
						sDifference = 0;
					}else{
						sDifference = sDifference-(ROOMLIGHT_MINIMUM+ROOMLIGHT_MAXIMUM)/2;
						stepChanges.add((ROOMLIGHT_MINIMUM+ROOMLIGHT_MAXIMUM)/2);
					}
				}
				//sDifference is not 0 then leave it as it....
				if(sDifference>0){
					stepChanges.clear();
					sDifference = currentRealPower-base_power;
					stepChanges.add(base_power);
					stepChanges.add(sDifference);
				}else{
					//do nothing...
				}

			}else{
				//An appliance has been turned off... now check which one...
				System.out.println("How come base power is giving it in negative?");
			}
			//			System.out.println(sDifference);
			return stepChanges;
		}else{
			//			if(previousRealPower < 39)
			//				base_power = previousRealPower;
			return null;
		}
	}

	public void trainAndClassifySensorData(String trainFilename, String predictFilename){
		//data from arff files to be stored in instances
		Instances trainData = null;
		Instances predictData = null;
		Instances labeledData = null;

		//filters to filter the data
		Remove removeAttribute = new Remove();
		int[] attributes = new int[2]; //remove first two attributes
		attributes[0] = 0;
		attributes[1] = 1;
		removeAttribute.setAttributeIndicesArray(attributes);
		FilteredClassifier filteredClassifier = new FilteredClassifier();
		filteredClassifier.setFilter(removeAttribute);


		try {
			//load training file
			BufferedReader trainingReader = new BufferedReader(new FileReader(trainFilename));
			trainData = new Instances(trainingReader);
			//load unlabeled file ending with class label as ?
			BufferedReader predictReader = new BufferedReader(new FileReader(predictFilename));
			predictData = new Instances(predictReader);
			trainingReader.close();
			predictReader.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

		//setting class attribute
		trainData.setClassIndex(trainData.numAttributes() - 1);
		predictData.setClassIndex(trainData.numAttributes() - 1);
		//making a copy of the unlabeled data to use it inside loop
		labeledData = new Instances(predictData);
		//this is used to set options of the classifier in this case its U = Unpruned
		String[] options = new String[1];
		options[0] = "-U";
		//		options[1] = "1";

		try {
			//using classifiers that are implementation different algorithms
			J48 j48Classifier = new J48();
			j48Classifier.setOptions(options);
			BayesNet bayesNetClassifier = new BayesNet();
			RandomForest randomForestClassifier = new RandomForest();
			IB1 iB1Classifier = new IB1();
			LogitBoost logitBoostClassifier = new LogitBoost();
			VFI vfiClassifier = new VFI();
			//			filteredClassifier.setClassifier(bayesNetClassifier);
			//						filteredClassifier.setClassifier(randomForestClassifier);
			//												filteredClassifier.setClassifier(iB1Classifier);
			filteredClassifier.setClassifier(logitBoostClassifier);
			//									filteredClassifier.setClassifier(vfiClassifier);

			filteredClassifier.buildClassifier(trainData);
			//			j48Classifier.buildClassifier(trainData);

			//we can now evaluate how good the classifier is
			Evaluation evaluateClassifier = new Evaluation(trainData);
			//on a test data set
			//			evaluateClassifier.evaluateModel(j48Classifier, predictData);
			// using cross validation here it is 10 fold
			evaluateClassifier.crossValidateModel(filteredClassifier, trainData, 10, new Random(1));
			System.out.println(evaluateClassifier.toSummaryString("\nResults\n=======\n", false));
			//saving evaluation results into database for each classifiers...
			//			mySQLDB.updateTable("INSERT INTO heterogeneous_sensor_light_classifiers_results VALUES('heterogeneous_sensor_light_labeled_data_LogitBoost','"+ evaluateClassifier.toSummaryString("", false)+"')");

			//classifying instances using j48
			for (int i = 0; i < predictData.numInstances(); i++) {
				double predictedClass = filteredClassifier.classifyInstance(predictData.instance(i));
				labeledData.instance(i).setClassValue(predictedClass);
				//testing what class is being assigned...
				//				System.out.println(predictedClass + " -> " + predictData.classAttribute().value((int) predictedClass));
			}

			//now write the labeled file into an arff file...
			saveClassifiedDatatoMySQL(labeledData, "episensor_classified_sep", "episensor");
			//			System.out.println(labeledData.instance(1).value(2));
			//						saveARFFtoMySQL(trainData, "heterogeneous_sensors_training_data");
			//			BufferedWriter fileWriter = new BufferedWriter(new FileWriter("episensor_labeled.arff"));
			//			fileWriter.write(labeledData.toString());
			//			fileWriter.newLine();
			//			fileWriter.flush();
			//			fileWriter.close();

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void saveClassifiedDatatoMySQL(Instances classifiedData, String tableName, String sensorType){
		//for episensor data...
		if(sensorType.equalsIgnoreCase("episensor")){
			for (int i = 0; i < classifiedData.numInstances(); i++) {
				mySQLDB.updateTable("INSERT INTO "+tableName+" VALUES('"+classifiedData.instance(i).stringValue(0)+"','"+classifiedData.instance(i).value(1)+"',"+classifiedData.instance(i).value(2)+",'"+classifiedData.instance(i).stringValue(3)+"')");
			}
		}
		//for heterogeneous sensor data
		if(sensorType.equalsIgnoreCase("heterogeneoussensor")){
			for (int i = 0; i < classifiedData.numInstances(); i++) {
				mySQLDB.updateTable("INSERT INTO "+tableName+" VALUES('"+classifiedData.instance(i).stringValue(0)+"',"+classifiedData.instance(i).value(1)+","+classifiedData.instance(i).value(2)+","+classifiedData.instance(i).value(3)+",'"+classifiedData.instance(i).stringValue(4)+"')");
			}
		}

	}


	public static void main(String[] args) {
		MachineLearningABLE test = new MachineLearningABLE();

		test.connectWithMysqlServer();
		//		//episensor training set...
		test.retreiveRecordsAndFillArrayList("select * from episensor_realpower_training_data", "episensor_training");
		//episensor unlabeled data set...
		test.retreiveRecordsAndFillArrayList("SELECT eData.SenseDate, eInfo.Name, eData.SenseValue FROM EpiSensorData AS eData " +
				"INNER JOIN EpiSensorInfo AS eInfo ON eData.IODeviceAddress = eInfo.IODeviceAddress " +
				"WHERE (eData.IODeviceAddress = 501) " +
				"AND eData.SenseDate > '2012-09-01 00:00:00.000' AND eData.SenseDate < '2012-09-30 23:59:00.000' " +
				"ORDER BY eData.SenseDate", "EpiSensor");
		test.saveDataAsARFF();
		test.trainAndClassifySensorData("episensor_realpower_training_data.arff", "episensor_unlabeled_data.arff");

		//heterogeneous sensor data
		//		test.retreiveRecordsAndFillArrayList("SELECT sData.SenseDateTime, nInfo.NodeInfo, sInfo.ModalityInfo, sData.SensorValue " +
		//				"FROM SixthSensorData AS sData JOIN SixthModalityInfo AS sInfo ON sData.ModalityID = sInfo.ModalityID " +
		//				"JOIN SixthNodeInfo AS nInfo ON sData.NodeID = nInfo.NodeID  " +
		//				"WHERE sData.SenseDateTime > '2012-08-21 00:00:00.000' AND sData.SenseDateTime < '2012-08-29 00:00:00.000' " +
		//				"ORDER BY sData.SenseDateTime;", "heterogeneoussensor");
		//		test.saveDataAsARFF();

		//training heterogeneous light data here...
		//		test.retreiveRecordsAndFillArrayList("select * from heterogeneous_sensors_training_data", "heterogeneoussensor_training");
		//		test.retreiveRecordsAndFillArrayList("SELECT sData.SenseDateTime, sData.NodeID, sData.ModalityID, sData.SensorValue " +
		//				"FROM SixthSensorData AS sData JOIN SixthModalityInfo AS sInfo ON sData.ModalityID = sInfo.ModalityID " +
		//				"JOIN SixthNodeInfo AS nInfo ON sData.NodeID = nInfo.NodeID  WHERE (sInfo.ModalityInfo='Light' " +
		//				"AND (sData.NodeID=10 OR sData.NodeID=11 OR sData.NodeID=12 OR sData.NodeID=14) ) " +
		//				"AND (sData.SenseDateTime > '2012-08-21 00:00:00.000' AND sData.SenseDateTime < '2012-08-29 00:00:00.000') " +
		//				"ORDER BY sData.SenseDateTime", "heterogeneoussensor");
		//		test.saveDataAsARFF();
		//		test.trainAndClassifySensorData("heterogeneous_sensor_training_data.arff", "heterogeneous_sensor_unlabeled_data.arff");

		test.mySQLDB.closeConnection();

	}

}
